/*
  ==============================================================================

   This file is part of the JUCE library - "Jules' Utility Class Extensions"
   Copyright 2004-11 by Raw Material Software Ltd.

  ------------------------------------------------------------------------------

   JUCE can be redistributed and/or modified under the terms of the GNU General
   Public License (Version 2), as published by the Free Software Foundation.
   A copy of the license is included in the JUCE distribution, or can be found
   online at www.gnu.org/licenses.

   JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
   A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

  ------------------------------------------------------------------------------

   To release a closed-source product which uses JUCE, commercial licenses are
   available: visit www.rawmaterialsoftware.com/juce for more information.

  ==============================================================================
*/

namespace D3D9Rendering
{

struct Target
{
    Target (D3D9Context& context_, LPDIRECT3DSURFACE9 Surface_, int width, int height) noexcept
        : context (context_), frameBufferSurface (Surface_), bounds (width, height)
    {}

    Target (D3D9Context& context_, D3D9FrameBuffer& frameBuffer_, const Point<int>& origin) noexcept
        : context (context_), frameBufferSurface (frameBuffer_.getFrameBufferSurface()),
          bounds (origin.x, origin.y, frameBuffer_.getWidth(), frameBuffer_.getHeight())
    {
        jassert (frameBufferSurface != NULL); // trying to render into an uninitialised framebuffer object.
    }

    Target (const Target& other) noexcept
        : context (other.context), frameBufferSurface (other.frameBufferSurface), bounds (other.bounds)
    {}

    Target& operator= (const Target& other) noexcept
    {
        frameBufferSurface = other.frameBufferSurface;
        bounds = other.bounds;
        return *this;
    }

    void makeActive() const noexcept
    {
		D3D9Helpers::pd3dDevice->SetRenderTarget(0, frameBufferSurface);

		D3DVIEWPORT9 vp;
		vp.X      = 0;
		vp.Y      = 0;
		vp.Width  = bounds.getWidth();
		vp.Height = bounds.getHeight();
		vp.MinZ   = 0.0f;
		vp.MaxZ   = 1.0f;

		D3D9Helpers::pd3dDevice->SetViewport( &vp );
		D3D9Helpers::pd3dDevice->SetRenderState( D3DRS_ZENABLE, D3DZB_FALSE );
    }

    D3D9Context& context;
    LPDIRECT3DSURFACE9 frameBufferSurface;
    Rectangle<int> bounds;
};



class NonShaderContext   : public LowLevelGraphicsSoftwareRenderer
{
public:
    NonShaderContext (const Target& target_, const Image& image_)
        : LowLevelGraphicsSoftwareRenderer (image_), target (target_), image (image_)
    {}

    ~NonShaderContext()
    {

        D3D9Texture texture;
        texture.loadImage (image);
        texture.bind();		// Set Texture

		D3D9Helpers::pd3dDevice->SetRenderState( D3DRS_LIGHTING, FALSE );

        target.makeActive();
        target.context.copyTexture (target.bounds, Rectangle<int> (texture.getWidth(),
                                                                   texture.getHeight()),
                                    target.bounds.getWidth(), target.bounds.getHeight());

		texture.unbind();

    }

private:
    Target target;
    Image image;

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NonShaderContext);
};

LowLevelGraphicsContext* createD3D9Context (const Target&);
LowLevelGraphicsContext* createD3D9Context (const Target& target)
{
   #if JUCE_USE_D3D9_SHADERS
    if (target.context.areShadersAvailable())
        return new ShaderContext (target);
   #endif

    Image tempImage (Image::ARGB, target.bounds.getWidth(), target.bounds.getHeight(), true, SoftwareImageType());
    return new NonShaderContext (target, tempImage);
}

}

//==============================================================================
LowLevelGraphicsContext* createD3D9GraphicsContext (D3D9Context& context)
{
    return createD3D9GraphicsContext (context, context.getFrameBufferSurface(),
                                        context.getWidth(), context.getHeight());
}

LowLevelGraphicsContext* createD3D9GraphicsContext (D3D9Context& context, D3D9FrameBuffer& target)
{
    using namespace D3D9Rendering;
    return createD3D9Context (Target (context, target, Point<int>()));
}

LowLevelGraphicsContext* createD3D9GraphicsContext (D3D9Context& context, LPDIRECT3DSURFACE9 Surface, int width, int height)
{
    using namespace D3D9Rendering;
    return createD3D9Context (Target (context, Surface, width, height));
}
